﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SG.Domain;
using System.Collections;
using SG.Logic.Stock;

namespace SG.Logic
{
    public class ExpPolitica:ContextManager
    {

        #region Constructor
        public ExpPolitica():base() { 
        
        }
        public ExpPolitica(SGEntities cont): base(cont) { 
        
        }

        #endregion 
        #region CalculoPoliticas
        public double CalcularFactorSeguridad(double nivelServicio)
        {
            double factorSeguridad;

            if (nivelServicio.ToString("{0:0.0}") == "0.5") factorSeguridad = 0;
            else if (nivelServicio.ToString("0.0") == "0,6") factorSeguridad = 0.25;
            else if (nivelServicio.ToString("0.0") == "0,7") factorSeguridad = 0.52;
            else if (nivelServicio.ToString("0.0") == "0,8") factorSeguridad = 0.84;
            else if (nivelServicio.ToString("0.0") == "0,9") factorSeguridad = 1.24;
            else if (nivelServicio.ToString("0.00") == "0,95") factorSeguridad = 1.64;
            else if (nivelServicio.ToString("0.000") == "0,975") factorSeguridad = 1.96;
            else if (nivelServicio.ToString("0.000") == "0,999") factorSeguridad = 3.08;
            else if (nivelServicio.ToString("0.0000") == "0,9998") factorSeguridad = 3.6;
            else factorSeguridad = 0;
            return factorSeguridad;
        }

        public int CalcularStockDisponible(Articulo articulo)
        {
            return (articulo.Stock + (articulo.StockARecepcionar.HasValue ? articulo.StockARecepcionar.Value : 0) - (articulo.StockComprometido.HasValue?articulo.StockComprometido.Value:0));
        }

        public int CalcularCantidadAPedir(double costoPedido, double demandaAnual, double costoAdquisicion, double tasaAbastecimiento, int s, int stockDisponible, int diasPeriodo)
        {
            int Q = 0;      // Lote optimo                   
            
            Q = calcularEOQ(costoPedido, demandaAnual, costoAdquisicion, tasaAbastecimiento, diasPeriodo);

            return Q;
        }

        public int calcularEOQ(double costoPedido, double demandaReal, double costoAdquisicion, double tasaAbastecimiento, int diasPeriodo)
        {
            //calcula  el lote optimo en un periodo
            double H;   //Costo anual de Almacenamiento
            H = costoAdquisicion * tasaAbastecimiento;
            return (int)(Math.Sqrt(2 * costoPedido * demandaReal / H));
        }

        public int CalcularCantidadAPedirsS(int stockDisponible, int stockMaximo, int puntoPedido)
        {
            if (stockDisponible < puntoPedido)
                return (int)(stockMaximo - stockDisponible);
            else return 0;
        }

        public double CalcularDesviacionEstandar(Articulo articulo, int tpoDemoraProveedor) // double destandar = new Double( estrategia.calcularDemandaAnual(producto)[1]).intValue(); 
        {
            ExpArticulo exp = new ExpArticulo();
            Periodo ultimoPeriodo = new Periodo();            
            ultimoPeriodo = exp.GetLastPeriodo(articulo.Id);
            int periodo = ultimoPeriodo.CantDias;
            //double desviacionEstandar = ultimoPeriodo.Error.DesviacionEstandar;
            double desviacionEstandar = Math.Sqrt(ultimoPeriodo.Error.MSEn); // Saco la Raiz de MSen
            return desviacionEstandar * tpoDemoraProveedor / periodo;
        }
        
        #endregion

        public void CalcularPolitica (Articulo articulo)
        {
            int politica = articulo.IdPolitica;
            if (politica == 1)
                CalcularPoliticaSQ(articulo);
            else
                CalcularPoliticaSR(articulo);

        }

        public void CalcularPoliticaSQ(Articulo articulo)
        {
            Proveedor proveedor = new Proveedor();         
            ArticuloProveedor artProv;
            double costoUnitario, demandaEstimada = 0, costoPedido, tasaAlmacenamiento, nivelServicio, factorSeguridad, desviacionEstandar, demandaEstimadaTpoEntrega; //demanda esperada en el tiempo de entrega
            int SS, /*Stock de Seguridad*/ s,/*Punto de Pedido*/ Q,/*Cantidad a pedir*/ stockDisponible, periodo = 364, tpoDemoraProveedor;

            if (articulo.ArticuloProveedor.Count == 0) return;

            proveedor = articulo.ArticuloProveedor.First().Proveedor;

            artProv = articulo.ArticuloProveedor.First();

            nivelServicio = articulo.NivelServicio.Valor;
            costoUnitario = artProv.CostoAdquisicion;

            /*Obtener demanda estimada en el periodo*/
            ExpArticulo exp = new ExpArticulo();
            Periodo ultimoPeriodo = new Periodo();
            ultimoPeriodo = exp.GetLastPeriodoParaPolitica(articulo.Id);
            demandaEstimada = ultimoPeriodo.Promedio;
            periodo = ultimoPeriodo.CantDias;

            double demandaAnual = ultimoPeriodo.Demanda;// Demanda Anual o Mensual;
            costoPedido = artProv.CostoEmision; 
            tasaAlmacenamiento = articulo.TasaAlmacenamiento; // tasa recargo por almacenamiento (r) 
            tpoDemoraProveedor = (int)artProv.TiempoEspera;

            // Para calcular Punto Pedido
            factorSeguridad = CalcularFactorSeguridad(nivelServicio);
            desviacionEstandar = CalcularDesviacionEstandar(articulo, tpoDemoraProveedor); // Desviacion Estandar de la Demanda durante el Tiempo de Entrega
            SS =(int) Math.Ceiling(factorSeguridad * desviacionEstandar); // Stock Seguridad = Factor de Seguridad * Desviacion Estandar de la Demanda durante el Tiempo de Entrega
            demandaEstimadaTpoEntrega = demandaEstimada * (float)tpoDemoraProveedor / (float)periodo; // demanda esperada durante el tiempo de entrega
            s = (int)Math.Ceiling(SS + demandaEstimadaTpoEntrega); // Calculo el Punto de Pedido = Stock Seguridad + Prediccion de la demanda durante el tiempo de entrega

            // Para calcular Cantidad a Pedir
            stockDisponible = CalcularStockDisponible(articulo);
            Q = CalcularCantidadAPedir(costoPedido, demandaAnual, costoUnitario, tasaAlmacenamiento, s, stockDisponible, periodo);
            
         
            int numeroPedidos = (int)(demandaAnual / Q);

            //Guardo Articulo
            articulo.StockSeguridad = SS;
            articulo.PuntoPedido = s;
            articulo.LoteOptimo = Q;
            articulo.NumeroPedidos = numeroPedidos;

            if (stockDisponible < s)
             {
                OrdenCompra oc = new OrdenCompra();
                DetalleOrdenDeCompra doc = new DetalleOrdenDeCompra();
                doc.Cantidad = Q;
                oc.IdProveedor = proveedor.Id;
                doc.IdArticulo = articulo.Id;
                oc.Estado = "Generada";
                oc.DetalleOrdenDeCompra.Add(doc);
                exp.SaveOrdenCompra(oc);
                Context.SaveChanges();
             }
        }

        private void CalcularPoliticaSR(Articulo articulo)
        {
            // Se hace el pedido al final del periodo R
            Proveedor proveedor;
            ArticuloProveedor artProv;
            double demandaEstimada;
            double factorSeguridad, desviacionEstandar, demandaEstimadaTpoEntrega;
            int SS, /*stock de seguridad*/ stockDisponible, stockMaximo, Q;

            if (articulo.ArticuloProveedor.Count == 0) throw new Exception("No hay proveedor");

            proveedor = articulo.ArticuloProveedor.First().Proveedor;
            artProv = articulo.ArticuloProveedor.First();

            int tiempoR = proveedor.TiempoR.Value; //esta en dias
            int tpoDemoraProveedor = (int)artProv.TiempoEspera; //esta en dias
            DateTime ultimaVisita = proveedor.FechaUltimaVisita.Value;
            DateTime fechaSistema = DateTime.Now;
            double nivelServicio = articulo.NivelServicio.Valor;
            

            /*Obtener demanda estimada en el periodo*/
            ExpArticulo exp = new ExpArticulo();
            Periodo ultimoPeriodo = new Periodo();
            ultimoPeriodo = exp.GetLastPeriodoParaPolitica(articulo.Id);
            demandaEstimada = ultimoPeriodo.Promedio;
            int periodo = ultimoPeriodo.CantDias;
            //tendria que comparar la fecha det sistema con ultimaVisita + tmpo_R

            long a = tiempoR * 24 * 60 * 60 * 1000;
            long b = ultimaVisita.Ticks;
            DateTime nuevo = new DateTime(b + a);

            //if (fechaSistema.Date == nuevo.Date && fechaSistema.Month == nuevo.Month && fechaSistema.Year == nuevo.Year)
            //{
                factorSeguridad = CalcularFactorSeguridad(nivelServicio);
                desviacionEstandar = CalcularDesviacionEstandar(articulo, tpoDemoraProveedor + tiempoR);
                demandaEstimadaTpoEntrega = (tpoDemoraProveedor + tiempoR) * demandaEstimada / periodo;                
                SS = (int)(factorSeguridad * desviacionEstandar);
                stockDisponible = CalcularStockDisponible(articulo);
                stockMaximo = (int)demandaEstimadaTpoEntrega + SS; // Calcular Stock Max SR
                Q = stockMaximo - stockDisponible; // Calcular Cantidad A Pedir SR

                proveedor.FechaUltimaVisita = fechaSistema;
                articulo.StockSeguridad = SS;
                articulo.StockMaximo = stockMaximo;

                if (Q > 0)
                {
                    articulo.LoteOptimo = Q;
                    OrdenCompra oc = new OrdenCompra();
                    DetalleOrdenDeCompra doc = new DetalleOrdenDeCompra();
                    doc.Cantidad = Q;
                    oc.IdProveedor = proveedor.Id;
                    doc.IdArticulo = articulo.Id; 
                    oc.Estado = "Generada";
                    oc.DetalleOrdenDeCompra.Add(doc);
                    exp.SaveOrdenCompra(oc);
                    Context.SaveChanges();
                }
  //          }

        }

    }
}
